using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Security.Cryptography;

namespace IWS.Lib.SingleSignOn
{
	/// <summary>
	/// Utility class used for miscellaneous tasks inside SingleSignOn. 
	/// </summary>
	/// <remarks>Normally this functionality would be provided by a third party application block.  For simplicity it is used here in SingleSignOn.</remarks>
	public static class Utility
	{
		/// <summary>
		/// Returns a randomized alphanumeric string at the specified length.
		/// </summary>
		/// <param name="length">Length of characters for the new string.</param>
		/// <returns>Randomized alphanumeric string of the specified length</returns>
		/// <exception cref="ArgumentOutOfRangeException"/>
		public static string GetRandomString(int length)
		{
			if (length <= 0)
				throw new ArgumentOutOfRangeException("length", "Length must be greater than zero.");

			//returns random string of length specified with characters including: 0-9, a-z, A-Z
			char[] ca = new char[length];
			byte[] random = new Byte[length];
			//RNGCryptoServiceProvider is an implementation of a random number generator.

			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetBytes(random); // The array is now filled with cryptographically strong random bytes.

			for (int i = 0; i < length; i++)
			{
				bool found = false;
				int rand = (int)random[i];
				char ch = (char)rand;
				while (!found)
				{
					if (((ch >= '0' && ch <= '9')) || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
					{
						found = true;
					}
					else
					{
						//get a new random int. 
						byte[] single = new byte[1];
						rng.GetBytes(single);
						rand = single[0];
						ch = (char)rand;
					}
				}
				char ci = (char)rand;
				ca[i] = ci;
			}
			string s = new string(ca);
			return s;
		}

		/// <summary>
		/// Return configured value of whether or not logging should be performed. 
		/// </summary>
		public static bool AllowExceptionLogging
		{
			get { return Properties.Settings.Default.LogExceptions; }
		}

		/// <summary>
		/// Log an exception to a flat tab delimited file.
		/// </summary>
		/// <param name="assembly">Assembly where the exception occurred. Typically System.Reflection.GetExecutingAssembly()</param>
		/// <param name="ex">Exception to log</param>
		/// <remarks>Optionally you can include the stack trace and configure the log file paths via the web config.</remarks>
		public static void LogException(System.Reflection.Assembly assembly, Exception ex)
		{
			// <param name="filename">Use Assembly.ManifestModule.Name for filename</param>
			// <param name="fullname">Assembly.FullName for version</param>
			string msg = DateTime.Now.ToUniversalTime() + "\t" + ExtractVersionFromFullname(assembly.FullName) + "\t" + assembly.ManifestModule.Name + "\t" + ex.Message;
			
			if (Properties.Settings.Default.LogStackTrace)
				msg += "\t" + ex.StackTrace;

			LogMessage(msg);
		}

		/// <summary>
		/// Log a message to a flat text file.
		/// </summary>
		/// <param name="message">Message to append.</param>
		/// <remarks>All messages are prepended with a carriage return (\n)</remarks>
		public static void LogMessage(string message)
		{
			string date = DateTime.Today.ToUniversalTime().ToShortDateString().Replace("/", "-");
			string filename = Properties.Settings.Default.LogFile.Replace("#date#", date);
			if (filename.StartsWith("~"))
				filename = System.Web.HttpContext.Current.Server.MapPath(filename); //map virtual resolution

			if (Directory.Exists(Path.GetDirectoryName(filename)))
				File.AppendAllText(filename, "\n" + message);
		}
		

		/// <summary>
		/// Parses an assembly full name for its version.
		/// </summary>
		/// <remarks>This function is ntended for medium trust environments as a work around to get information of the executing assembly without officially using reflection.</remarks>
		/// <param name="fullname">Assembly.FullName</param>
		/// <returns></returns>
		public static string ExtractVersionFromFullname(string fullname)
		{
			string version = "";
			string[] assemblyinfo = fullname.Split(',');
			for (int ix = 0; ix < assemblyinfo.Length; ix++)
			{
				string[] keyvalue = assemblyinfo[ix].Trim().Split('=');
				string key = keyvalue[0].Trim().ToUpper();
				if (key == "VERSION")
				{
					version = keyvalue[1].Trim();
				}
			}
			return version;
		}
	}
}
